package com.alex.statistics.method.grouping;


import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
public class GroupingAnalyzer {

    /**
     * 统计分位数法分组
     * @param data 输入数据
     * @param quantiles 分位数数组，如[0.25, 0.5, 0.75]表示四分位数
     * @return 分组边界值
     */
    public List<Double> quantileGrouping(List<Double> data, double[] quantiles) {
        List<Double> sortedData = data.stream()
                .sorted()
                .toList();

        int n = sortedData.size();
        List<Double> boundaries = new ArrayList<>();

        for (double q : quantiles) {
            double pos = q * (n - 1);
            int lower = (int) Math.floor(pos);
            int upper = (int) Math.ceil(pos);

            if (lower == upper) {
                boundaries.add(sortedData.get(lower));
            } else {
                double lowerValue = sortedData.get(lower);
                double upperValue = sortedData.get(upper);
                boundaries.add(lowerValue + (pos - lower) * (upperValue - lowerValue));
            }
        }

        return boundaries;
    }

    /**
     * 自然断点法分组 (Jenks Natural Breaks)
     * @param data 输入数据
     * @param k 分组数量
     * @return 分组边界值
     */
    public List<Double> naturalBreaksGrouping(List<Double> data, int k) {
        if (k <= 1) {
            return Collections.emptyList();
        }

        List<Double> sortedData = data.stream()
                .sorted()
                .collect(Collectors.toList());

        int n = sortedData.size();
        double[][] matrix = new double[n + 1][k + 1];
        double[][] lowerClass = new double[n + 1][k + 1];

        // 初始化矩阵
        for (int i = 1; i <= n; i++) {
            matrix[i][1] = variance(sortedData.subList(0, i));
            lowerClass[i][1] = 0;
        }

        // 动态规划计算最优分组
        for (int j = 2; j <= k; j++) {
            for (int i = j; i <= n; i++) {
                matrix[i][j] = Double.MAX_VALUE;

                for (int m = j - 1; m < i; m++) {
                    double variance = matrix[m][j - 1] + variance(sortedData.subList(m, i));
                    if (variance < matrix[i][j]) {
                        matrix[i][j] = variance;
                        lowerClass[i][j] = m;
                    }
                }
            }
        }

        // 回溯找出分组边界
        List<Double> boundaries = new ArrayList<>();
        int[] breaks = new int[k - 1];
        findBreaks(breaks, lowerClass, n, k);

        for (int b : breaks) {
            boundaries.add(sortedData.get(b));
        }

        return boundaries;
    }

    private void findBreaks(int[] breaks, double[][] lowerClass, int n, int k) {
        if (k == 1) {
            return;
        }

        int breakPoint = (int) lowerClass[n][k];
        breaks[k - 2] = breakPoint;
        findBreaks(breaks, lowerClass, breakPoint, k - 1);
    }

    private double variance(List<Double> data) {
        if (data.isEmpty()) return 0;

        double mean = data.stream().mapToDouble(Double::doubleValue).average().orElse(0);
        return data.stream()
                .mapToDouble(x -> Math.pow(x - mean, 2))
                .sum();
    }

    /**
     * 等间距法分组
     * @param data 输入数据
     * @param interval 间距大小
     * @return 分组边界值
     */
    public List<Double> equalIntervalGrouping(List<Double> data, double interval) {
        if (interval <= 0) {
            throw new IllegalArgumentException("Interval must be positive");
        }

        double min = data.stream().min(Double::compare).orElse(0.0);
        double max = data.stream().max(Double::compare).orElse(min + interval);

        List<Double> boundaries = new ArrayList<>();
        double current = min;

        while (current < max) {
            boundaries.add(current);
            current += interval;
        }

        boundaries.add(max); // 确保包含最大值
        return boundaries;
    }

    /**
     * 自定义规则法分组
     * @param data 输入数据
     * @param customBoundaries 自定义边界值
     * @return 分组边界值
     */
    public List<Double> customRuleGrouping(List<Double> data, List<Double> customBoundaries) {
        if (customBoundaries == null || customBoundaries.isEmpty()) {
            throw new IllegalArgumentException("Custom boundaries cannot be empty");
        }

        List<Double> sortedBoundaries = new ArrayList<>(customBoundaries);
        Collections.sort(sortedBoundaries);
        return sortedBoundaries;
    }

    /**
     * 根据边界值计算分组统计
     * @param data 输入数据
     * @param boundaries 分组边界值
     * @return 分组统计结果
     */


    public Map<String, Integer> calculateGroupStats(List<Double> data, List<Double> boundaries) {
        if (data == null || data.isEmpty()) {
            return Map.of();
        }

        if (boundaries == null || boundaries.isEmpty()) {
            return Map.of("All", data.size());
        }

        // 获取数据最小值和最大值
        double minVal = Collections.min(data);
        double maxVal = Collections.max(data);

        // 处理边界值：去重、排序、确保在数据范围内
        List<Double> sortedBoundaries = boundaries.stream()
                .filter(Objects::nonNull)
                .distinct()
                .sorted()
                .filter(b -> b > minVal && b < maxVal) // 只保留在(minVal, maxVal)范围内的边界
                .collect(Collectors.toList());

        Map<String, Integer> result = new LinkedHashMap<>();

        if (sortedBoundaries.isEmpty()) {
            // 如果所有边界都在数据范围外，返回整个范围
            return Map.of(String.format("[%.2f,%.2f]", minVal, maxVal), data.size());
        }

        // 第一个区间：[minVal, firstBoundary)
        double firstBoundary = sortedBoundaries.get(0);
        result.put(String.format("[%.2f,%.2f)", minVal, firstBoundary),
                (int) data.stream().filter(x -> x >= minVal && x < firstBoundary).count());

        // 中间区间：[lower, upper)
        for (int i = 1; i < sortedBoundaries.size(); i++) {
            double lower = sortedBoundaries.get(i - 1);
            double upper = sortedBoundaries.get(i);
            result.put(String.format("[%.2f,%.2f)", lower, upper),
                    (int) data.stream().filter(x -> x >= lower && x < upper).count());
        }

        // 最后一个区间：[lastBoundary, maxVal]
        double lastBoundary = sortedBoundaries.get(sortedBoundaries.size() - 1);
        result.put(String.format("[%.2f,%.2f]", lastBoundary, maxVal),
                (int) data.stream().filter(x -> x >= lastBoundary && x <= maxVal).count());

        return result;
    }
}